﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data;

namespace yule.Data
{
    /// <summary>
    /// 事务
    /// </summary>
    public abstract class Transaction : IExecuteNonQuery, IExecuteQuery, IDisposable
    {
        protected abstract DbTransaction DbTransaction { get; }

        private bool _isEnd = false;

        private IList<TransactionAction> list = new List<TransactionAction>();

        private class TransactionAction
        {
            public string cmdText { get; set; }
            public IList<DbParameter> Parameters { get; set; }
            public Action<string> Action1 { get; set; }
            public Action<string, IList<DbParameter>> Action2 { get; set; }
        }


        /// <summary>
        /// 提交
        /// </summary>
        public void Commit()
        {
            this.DbTransaction.Commit();
            this._isEnd = true;

            foreach (TransactionAction ta in this.list)
            {
                if (ta.Action1 != null)
                {
                    ta.Action1(ta.cmdText);
                }
                if (ta.Action2 != null)
                {
                    ta.Action2(ta.cmdText, ta.Parameters);
                }
            }

        }

        /// <summary>
        /// 回滚
        /// </summary>
        public void Rollback()
        {
            this.DbTransaction.Rollback();
            this._isEnd = true;
        }

        /// <summary>
        /// 执行事务
        /// </summary>
        /// <param name="cmdText"></param>
        /// <returns></returns>
        public int ExecuteNonQuery(string cmdText, Action<string> action = null)
        {
            try
            {
                int val = this.ExecuteNonQuery(cmdText, null);

                if (val > 0 && action != null)
                {
                    TransactionAction ta = new TransactionAction()
                    {
                        Action1 = action,
                        cmdText = cmdText
                    };
                    this.list.Add(ta);
                }

                return val;

            }
            catch
            {
                this.Dispose();
                throw;
            }
        }


        /// <summary>
        /// 执行事务
        /// </summary>
        /// <param name="cmdText"></param>
        /// <param name="commandParameters"></param>
        /// <returns></returns>
        public int ExecuteNonQuery(string cmdText, IList<DbParameter> commandParameters, Action<string, IList<DbParameter>> action = null)
        {
            try
            {
                int val = this.ExecuteNonQuery(System.Data.CommandType.Text, cmdText, commandParameters);

                if (val > 0 && action != null)
                {
                    TransactionAction ta = new TransactionAction()
                    {
                        Action2 = action,
                        cmdText = cmdText,
                        Parameters = commandParameters
                    };
                    this.list.Add(ta);
                }

                return val;
            }
            catch
            {
                this.Dispose();
                throw;
            }
        }

        /// <summary>
        /// 执行事务
        /// </summary>
        /// <param name="cmdType"></param>
        /// <param name="cmdText"></param>
        /// <param name="commandParameters"></param>
        /// <returns></returns>
        public int ExecuteNonQuery(System.Data.CommandType cmdType, string cmdText, IList<DbParameter> commandParameters, Action<string, IList<DbParameter>> action = null)
        {
            try
            {
                DbCommand cmd = this.DbTransaction.Connection.CreateCommand();

                DBHelper.PrepareCommand(cmd, this.DbTransaction.Connection, this.DbTransaction, cmdType, cmdText, commandParameters);
                int val = cmd.ExecuteNonQuery();

                if (val > 0 && action != null)
                {
                    TransactionAction ta = new TransactionAction()
                    {
                        Action2 = action,
                        cmdText = cmdText,
                        Parameters = commandParameters
                    };
                    this.list.Add(ta);
                }

                cmd.Parameters.Clear();

                return val;

            }
            catch
            {
                this.Dispose();
                throw;
            }
        }




        #region IDisposable 成员

        public void Dispose()
        {
            if (this.DbTransaction != null)
            {
                if (this._isEnd == false)
                {
                    this.DbTransaction.Rollback();
                }
            }
        }

        #endregion

        public abstract DbParameter CreateDbParameter(string parameterName, object value, DbType dbType, ParameterDirection direction = ParameterDirection.Input);


        #region IExecuteQuery 成员

        public DataTable ExecuteDataTable(string cmdText)
        {
            return this.ExecuteDataTable(cmdText, null);
        }

        public DataTable ExecuteDataTable(string cmdText, IList<DbParameter> commandParameters)
        {
            return this.ExecuteDataTable(CommandType.Text, cmdText, commandParameters);
        }

        protected abstract System.Data.Common.DbDataAdapter CreateDbDataAdapter(System.Data.Common.DbCommand cmd);

        public DataTable ExecuteDataTable(CommandType cmdType, string cmdText, IList<DbParameter> commandParameters)
        {
            DbCommand cmd = this.DbTransaction.Connection.CreateCommand();
            try
            {
                DBHelper.PrepareCommand(cmd, this.DbTransaction.Connection, null, cmdType, cmdText, commandParameters);
                DbDataAdapter da = this.CreateDbDataAdapter(cmd);
                


                da.SelectCommand.CommandTimeout = 120;

                DataSet ds = new DataSet();
                da.Fill(ds);

                if (ds.Tables.Count > 0)
                {
                    return ds.Tables[0];
                }
                else
                {
                    return null;
                }

            }
            catch
            {
                this.Rollback();
                this.Dispose();
                throw;
            }
        }

        public object ExecuteScalar(string cmdText)
        {
            return this.ExecuteScalar(cmdText, null);
        }

        public object ExecuteScalar(string cmdText, IList<DbParameter> commandParameters)
        {
            return this.ExecuteScalar(CommandType.Text, cmdText, commandParameters);
        }

        public object ExecuteScalar(CommandType cmdType, string cmdText, IList<DbParameter> commandParameters)
        {
            DbCommand cmd = this.DbTransaction.Connection.CreateCommand();

            try
            {
                DBHelper.PrepareCommand(cmd, this.DbTransaction.Connection, null, cmdType, cmdText, commandParameters);
                object val = cmd.ExecuteScalar();
                cmd.Parameters.Clear();
                return val;
            }
            catch
            {
                this.Rollback();
                this.Dispose();
                throw;
            }
        }

        #endregion
    }
}